from Products.Five import BrowserView
from Acquisition import aq_inner
from urllib import urlencode
from datetime import *
from time import *

class CalDo(BrowserView):
    # Retrieves all events from the catalog
    def displayEvents(self,caltypes):
        context = aq_inner(self.context)
        query = {
            'meta_type':caltypes,
            'getCaleventtype':self.request.get('ev',''),
            'sort_on':self.request.get('srt','start'),
            'sort_order':self.request.get('srtord',''),
            }
        evs =  context.portal_catalog.searchResults(query)
        return evs

    # Defines a link by encoding the URL based on the title and start date
    def defLink(self, op):
        return ('?' + urlencode({'ev' : op,
                                 'srt' : self.request.get('srt','start'),
                                 'srtord': self.request.get('srtord',''),}))

    # Defines the columns in the event table
    def defCols(self,caltype):
        lastcol = ''
        colnames = ''
        if caltype == 'CalEvent':
            lastcol = ['getResmeetingroom','Meeting Room']
        else:
            lastcol = ['getContactno','Contact No']
        if caltype == 'CalOvertime':
            colnames = (['start','Start Time and Date'],
                        ['end','End Time and Date'],
                        ['overDuration','Duration'],
                        ['getApproved','Approved'])
        elif caltype == 'CalHoliday':
            colnames = (['start','Start Time and Date'],
                        ['end','End Time and Date'],
                        ['location','Location'],
                        lastcol,
                        ['getApproved','Approved'])
        else:
            colnames = (['start','Start Time and Date'],
                        ['end','End Time and Date'],
                        ['location','Location'],
                        lastcol)
        cols = []
        for c in colnames:
            cols.append(self.defCol(c))
        return cols

    # Defines each column header so that they can be clicked upon to sort the 
    # table contents by that column
    def defCol(self,colname):
        calord = self.setOrder(colname[0])
        return {'colname' : colname[1],
                'collink' : '?'+urlencode({'ev' : self.request.get('ev',''),
                                           'srt' : colname[0],
                                           'srtord': calord[0],}),
                'colimg' : calord[1]}

    # Sets the order of the column contents
    def setOrder(self, colname):
        if self.request.get('srt','start') == colname:
            if self.request.get('srtord') == 'reverse':
                return ['','up']
            else:
                return ['reverse','down']
        return ['','']

    def viewcalboxes(self):
        """Returns data structure containing calendar boxes to print on left"""
        if self.context.sameday():
            calbox = [{'monthyear' : self.context.start().strftime('%b %y'),
                       'dow' : self.context.start().strftime('%a'),
                       'dom' : self.context.start().strftime('%d'),
                       'time' : self.context.start().strftime('%H:%M') + ' - ' + self.context.end().strftime('%H:%M'),
                       'tobool' : 0}]
        else:
            calbox = [{'monthyear' : self.context.start().strftime('%b %y'),
                       'dow' : self.context.start().strftime('%a'),
                       'dom' : self.context.start().strftime('%d'),
                       'time' : self.context.start().strftime('%H:%M'),
                       'tobool' : 1},
                      {'monthyear' : '',
                       'dow' : self.context.end().strftime('%a'),
                       'dom' : self.context.end().strftime('%d'),
                       'time' : self.context.end().strftime('%H:%M'),
                       'tobool' : 0}]
            if self.context.start().strftime('%b%y') != self.context.end().strftime('%b%y'):
                calbox[1]['monthyear'] = self.context.end().strftime('%b %y')
        return calbox

# Class to construct the calendar contents
class CalendarView(BrowserView):
    # Retrieves the calendar contents in the form of a data structure
    def getcalendar(self):
        """Return calendar data structure"""
        # Retrieves the time period for the current month
        weeks = self.getweeks(self.viewedmonth(), self.viewedyear())

        context = aq_inner(self.context)

        # Queries the catalog, retrieving all events, holidays and overtimes in the time period
        results = context.portal_catalog.searchResults(
            meta_type = ('CalEvent','CalHoliday','CalOvertime'),
            start={
                'query':((weeks[-1][-1]['datetime']) + timedelta(days=1, minutes=-1)),
                'range':'max',
                },
            end={
                'query':(weeks[0][0]['datetime']),
                'range':'min',
                },
            sort_on = 'start',
            )
        # Goes through the result set from the query, inserting each result in to the calendar data structure
        for item in results:
            self.insertEvent(item,weeks)
        return weeks

    # Inserts an event into the calendar data structure
    def insertEvent(self, ev, weeks):
        """Inserts an event into the calendar data structure"""
        calev = { 'Title' : ev['Title'],
                  'type' : ev['meta_type'],
                  'startdate': ev['start'],
                  'enddate' : ev['end'],
                  'starttime' : ev['start'].strftime('%I:%M %p'),
                  'isStart' : True,
                  'location' : ev['location'],
                  'contactno' : ev['getContactno'],
                  'getlink' : ev.getURL(),
                  'duration': ev['overDuration'],
                  }

        if calev['type'] == "CalOvertime":
            calev['employee'] = ev['getEmployee']

        # Calculates the duration of the event in days
        duration = self.getDuration(datetime.fromtimestamp(ev['start']), datetime.fromtimestamp(ev['end']))
        index = 0

        # Check start date
        if datetime.fromtimestamp(ev['start']) >= weeks[0][0]['datetime']:
            sd = int(ev['start'].strftime('%d'))
            # Calculates the number of days away the event is from the first date on the calendar month
            index = self.getDuration(weeks[0][0]['datetime'], datetime.fromtimestamp(ev['start']))
            daysPassed = 0
        else:
            sd = weeks[0][0]['dateno']
            daysPassed = duration - self.getDuration(weeks[0][0]['datetime'], datetime.fromtimestamp(ev['end']))

        # Check end date
        if datetime.fromtimestamp(ev['end']) <= weeks[-1][-1]['datetime']:
            ed = int(ev['end'].strftime('%d'))
        else:
            ed = weeks[-1][-1]['dateno']

        # Get indexes for weeks data structure
        weekindex = index / 7
        dayindex = index % 7
        slot = -1

        # Loop through from start date of event until end date has been reached
        for w in range(weekindex, len(weeks)):
            while dayindex < 7:
                # Adds an overtime to the list of overtimes for that day
                if calev['type'] == "CalOvertime":
                    weeks[w][dayindex]['overtimes'].append(calev.copy())
                else:
                    daysevents = weeks[w][dayindex]['events']
                    # Allocates a slot for the event
                    if slot == -1:
                        # Finds first available slot for the current day if a slot has not been allocated yet
                        i = 0
                        while i < len(daysevents) and slot < 0:
                            if daysevents[i] == '':
                                slot = i
                                daysevents[i] = calev.copy()
                            i += 1
                        # If there are no free slots, then append to the end
                        if slot == -1:
                            slot = len(daysevents)
                            daysevents.append(calev.copy())
                    # If a slot has already been allocated to the event then add the event to that slot
                    else:
                        i = len(daysevents)
                        # Checks if the lower slots are populated with events
                        # If not, they are populated with empty events
                        while i <= slot:
                            daysevents.append('')
                            i += 1
                        # Adds the current event to it's own slot
                        daysevents[slot] = calev.copy()
                
                    # If the current day is not the start date then do not display start time
                    if (weeks[w][dayindex]['dateno'] != sd) or (daysPassed > 0):
                        daysevents[slot]['isStart'] = False

                # If current day is the end day then return
                if (weeks[w][dayindex]['dateno'] == ed) and (daysPassed == duration):
                    return
                
                # Increment counters
                dayindex += 1; daysPassed += 1
            
            # End of week, reset day index
            dayindex = 0

        return

    # Retrieves a list of weeks
    def getweeks(self, month, year):
        """Return list of weeks, list of days, list of events"""
        months = {'Jan' : 1, 'Feb' : 2, 'Mar' : 3, 'Apr' : 4, 'May' : 5, 'Jun' : 6,
                  'Jul' : 7, 'Aug' : 8, 'Sep' : 9, 'Oct' : 10, 'Nov' : 11, 'Dec' : 12}

        # first day of month = datetime constructor of demanded year and month
        firstdayofmonth = datetime(int(year), int(months[month]), 1)
        # wind back to a sunday
        iterate = firstdayofmonth - timedelta(days=int(firstdayofmonth.strftime('%w')))
        start   = firstdayofmonth - timedelta(days=int(firstdayofmonth.strftime('%w')))

        # Gets today's date for highlighting today on calendar
        today = datetime(int(datetime.today().strftime('%Y')),
                         int(datetime.today().strftime('%m')),
                         int(datetime.today().strftime('%d')))
        
        # initialise weeks list, fill with list of days
        weeks = []
        while int(iterate.strftime('%m')) == int(firstdayofmonth.strftime('%m')) or int(iterate.strftime('%m')) == int(start.strftime('%m')):
            days = []
            for dayno in range(0,7):
                if (iterate + timedelta(days=dayno)) == today:
                    days.append({'datetime' : (iterate + timedelta(days=dayno)), 
                                 'dateno': int((iterate + timedelta(days=dayno)).strftime('%d')),
                                 'events' : [],
                                 'overtimes' : [],
                                 'isToday' : True})
                else:
                    days.append({'datetime' : (iterate + timedelta(days=dayno)), 
                                 'dateno': int((iterate + timedelta(days=dayno)).strftime('%d')),
                                 'events' : [],
                                 'overtimes' : [],
                                 'isToday' : False})
            weeks.append(days)
            iterate += timedelta(days=7)
        return weeks

    def getDuration(self, start, end):
        # Sets the start and end datetime to midnight of their respective days
        start = datetime(int(start.strftime('%Y')),
                         int(start.strftime('%m')),
                         int(start.strftime('%d')))
        end = datetime(int(end.strftime('%Y')),
                       int(end.strftime('%m')),
                       int(end.strftime('%d')))
        # Returns the number of days from the start date to the end date
        return int((end - start).days)

    def listyears(self):
        thisyear = int(self.viewedyear());
        return [thisyear - 1, thisyear, thisyear + 1]

    def viewedyear(self):
        """Return string of month currently being viewed"""
        if self.request.get('viewyear'):
            return int(self.request.get('viewyear'))
        else:
            return int(strftime('%Y'))

    def listmonths(self):
        return ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',]

    def viewedmonth(self):
        """Return string of month currently being viewed"""
        if self.request.get('viewmonth'):
            return self.request.get('viewmonth')
        else:
            return strftime('%b')
        return 'Jul'

class CaleventtypeVocab(BrowserView):
    def __call__(self):
        return ['Meeting','Social Event','Work','Conference','Sprint', 'Other']
    # Class to return vocab

